package com.burst.reptile.framework.tencent;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import com.burst.reptile.common.constant.HttpStatus;
import com.burst.reptile.common.exception.CustomException;
import com.burst.reptile.framework.config.CrawlerConfig;
import com.burst.reptile.framework.upload.PutObjectProgressListener;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.UploadResult;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.TransferManagerConfiguration;
import com.qcloud.cos.transfer.Upload;

import lombok.extern.slf4j.Slf4j;

/**
 * 腾讯云cos
 * @author wujin
 *
 * @date: 2020年9月17日 下午2:57:17
 */
@Component
@Slf4j
public class CosPutObject {
	
	@Autowired
	private PutObjectProgressListener putObjectProgressListener;
	
	/**
	 * 获取上传客户端
	 * @return
	 */
	public COSClient getCosClient() {
		// 1 初始化用户身份信息（secretId, secretKey）。
		String secretId = CrawlerConfig.getSecretId();
		String secretKey = CrawlerConfig.getSecretKey();
		COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
		// 2 设置 bucket 的区域, COS 地域的简称请参照
		// https://cloud.tencent.com/document/product/436/6224
		// clientConfig 中包含了设置 region, https(默认 http), 超时, 代理等 set 方法, 使用可参见源码或者常见问题
		// Java SDK 部分。
		Region region = new Region("ap-chongqing");
		ClientConfig clientConfig = new ClientConfig(region);
		// 3 生成 cos 客户端。
		COSClient cosClient = new COSClient(cred, clientConfig);
		return cosClient;
	}

	/**
	 * 流式上传文件
	 * @param file 文件域
	 * @param bucketName 存储桶
	 * @param key key
	 * @param uuid 唯一标识
	 * @return
	 */
	public String put(MultipartFile file, String bucketName, String key, String uuid) {
		
		COSClient cosClient = getCosClient();
		FileInputStream fileInputStream = null;
		InputStream inputStream = null;
		TransferManager transferManager = null;
		try {
//			fileInputStream = (FileInputStream) file.getInputStream();
			inputStream = file.getInputStream();
			// Bucket的命名格式为 BucketName-APPID ，此处填写的存储桶名称必须为此格式
			// 从输入流上传(需提前告知输入流的长度, 否则可能导致 oom)
			ObjectMetadata objectMetadata = new ObjectMetadata();
			// 设置输入流长度为
			long size = file.getSize();
			objectMetadata.setContentLength(size);
			
			// 线程池大小，建议在客户端与 COS 网络充足（例如使用腾讯云的 CVM，同地域上传 COS）的情况下，设置成16或32即可，可较充分的利用网络资源
			// 对于使用公网传输且网络带宽质量不高的情况，建议减小该值，避免因网速过慢，造成请求超时。
			ExecutorService threadPool = Executors.newFixedThreadPool(16);
			// 传入一个 threadpool, 若不传入线程池，默认 TransferManager 中会生成一个单线程的线程池。
			transferManager = new TransferManager(cosClient, threadPool);
			// 设置高级接口的分块上传阈值和分块大小为10MB
			TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
			transferManagerConfiguration.setMultipartUploadThreshold(10 * 1024 * 1024);
			transferManagerConfiguration.setMinimumUploadPartSize(10 * 1024 * 1024);
			transferManager.setConfiguration(transferManagerConfiguration);
			PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, inputStream, objectMetadata);
			Upload upload = transferManager.upload(putObjectRequest);
			putObjectProgressListener.setUuid(uuid);
			putObjectProgressListener.setSize(size);
			upload.addProgressListener(putObjectProgressListener);
			// 等待传输结束（如果想同步的等待上传结束，则调用 waitForCompletion, 异步 waitForUploadResult）
//			upload.waitForCompletion();
			UploadResult uploadResult = upload.waitForUploadResult();
			return uploadResult.getETag();
		} catch (IOException e) {
			log.error("io上传文件IO异常");
			throw new CustomException("IO异常", HttpStatus.ERROR);
		} catch (CosServiceException e) {
			log.error("文件上传失败，{}", e.getErrorMessage());
			throw new CustomException(e.getErrorMessage(), HttpStatus.ERROR);
		} catch (CosClientException e) {
			log.error("文件上传失败，{}", e.getMessage());
			throw new CustomException(e.getMessage(), HttpStatus.ERROR);
		} catch (InterruptedException e) {
			log.error("文件上传失败，{}", e.getMessage());
			throw new CustomException(e.getMessage(), HttpStatus.ERROR);
		}finally {
			if(fileInputStream != null) {
				try {
					fileInputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(transferManager != null) {
				transferManager.shutdownNow();
			}
			cosClient.shutdown();
		}
	}
	
	/**
	 * 本地文件上传腾讯COS
	 * @param localFilePath 本地文件
	 * @param bucketName 存储桶
	 * @param key key
	 */
	public void localPut(String localFilePath, String bucketName, String key) {
		File localFile = new File(localFilePath);
		COSClient cosClient = getCosClient();
		cosClient.putObject(bucketName, key, localFile);
		cosClient.shutdown();
	}

}
